package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.application.enums.GenerateSnEnum;
import com.evil.application.mapper.ApplicationFormSnRuleMapper;
import com.evil.application.pojo.dto.form.rule.SnRuleConfigDTO;
import com.evil.application.pojo.dto.form.rule.SnRuleRespDTO;
import com.evil.application.pojo.dto.order.OrderSnSettingDTO;
import com.evil.application.pojo.entity.ApplicationForm;
import com.evil.application.pojo.entity.ApplicationFormSnRule;
import com.evil.application.service.ApplicationFormControlService;
import com.evil.application.service.ApplicationFormSnRuleService;
import com.evil.application.service.GenerateSnService;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.form.control.ControlBaseDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.enums.form.RuleTypeEnum;
import com.evil.common.application.enums.form.SnRuleTypeEnum;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 表单编号规则表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationFormSnRuleServiceImpl extends ServiceImpl<ApplicationFormSnRuleMapper, ApplicationFormSnRule> implements ApplicationFormSnRuleService {

    private final LoginUtil loginUtil;

    private final ApplicationFormControlService applicationFormControlService;

    private final GenerateSnService generateSnService;

    private final ApplicationLogServiceImpl logService;

    /**
     * 获取表单编号规则列表
     *
     * @param formId formId
     * @return ApplicationFormSnRule 列表
     */
    @Override
    public List<ApplicationFormSnRule> findByFormId(Long formId) {
        return this.baseMapper.findByFormId(formId);
    }

    /**
     * 获取表单编号规则基础信息列表
     *
     * @param formId formId
     * @return SnRuleRespDTO 列表
     */
    @Override
    public List<SnRuleRespDTO> findBaseByFormId(Long formId) {
        List<ApplicationFormSnRule> formSnRules = this.findByFormId(formId);
        List<SnRuleRespDTO> snRules = StreamUtil.transListT(formSnRules, SnRuleRespDTO::new);
        Predicate<SnRuleRespDTO> filter = e -> RuleTypeEnum.FORM_DATA.getId() == e.getRuleType()
                && null != e.getSnRuleConfig() && null != e.getSnRuleConfig().getControlId();

        // 根据前端要求，编号规则-表单字段，同步返回字段名称
        List<Long> controlIds = StreamUtil.transListFT(snRules, filter, e -> e.getSnRuleConfig().getControlId());
        Map<Long, String> controlTitleMap = StreamUtil.toMapKV(applicationFormControlService.findBaseInfoByControlIds(controlIds),
                ControlBaseDTO::getControlId, ControlBaseDTO::getControlTitle);

        snRules.stream().filter(filter).forEach(e -> e.getSnRuleConfig().setControlTitle(controlTitleMap
                .getOrDefault(e.getSnRuleConfig().getControlId(), "")));

        return snRules;
    }

    @Override
    public void modifyFormSnRule(ApplicationForm applicationForm, List<SnRuleRespDTO> snRules) {
        List<Long> ruleIds = StreamUtil.transListTF(snRules, SnRuleRespDTO::getApplicationFormRuleId, Objects::nonNull);

        List<ApplicationFormSnRule> formSnRuleList = this.baseMapper.findByFormId(applicationForm.getFormId());
        Map<Long, ApplicationFormSnRule> snRuleMap = StreamUtil.toMapK(formSnRuleList, ApplicationFormSnRule::getFormRuleId);

        // 新增编辑
        AtomicInteger i = new AtomicInteger(1);
        List<ApplicationFormSnRule> modifies = snRules.stream().map(e -> {
            ApplicationFormSnRule applicationFormSnRule;
            if (snRuleMap.containsKey(e.getApplicationFormRuleId())) {
                applicationFormSnRule = snRuleMap.get(e.getApplicationFormRuleId());
            } else {
                applicationFormSnRule = new ApplicationFormSnRule();
                applicationFormSnRule.setFormId(applicationForm.getFormId());
                applicationFormSnRule.setIsDeleted(SwitchEnum.NO.getId());
            }
            applicationFormSnRule.setRuleType(e.getRuleType());
            applicationFormSnRule.setSnRuleConfig(JSON.toJSONString(e.getSnRuleConfig()));
            applicationFormSnRule.setSort(i.getAndIncrement());
            return applicationFormSnRule;
        }).collect(Collectors.toList());

        // 删除
        modifies.addAll(StreamUtil.peekF(formSnRuleList, e -> !ruleIds.contains(e.getFormRuleId()), e -> e.setIsDeleted(SwitchEnum.YES.getId())));

        logService.saveAndCheck(this.baseMapper, ApplicationFormSnRule::getFormRuleId, modifies);
    }

    /**
     * 删除表单编号规则
     *
     * @param formId formId
     */
    @Override
    public void deleteByFormId(Long formId) {
        List<ApplicationFormSnRule> formSnRules = this.baseMapper.findByFormId(formId);
        formSnRules.forEach(e -> e.setIsDeleted(SwitchEnum.YES.getId()));
        logService.saveAndCheck(this.baseMapper, ApplicationFormSnRule::getFormRuleId, formSnRules);
    }

    /**
     * 复制表单编号规则
     *
     * @param source       复制源表单
     * @param target       复制目标表单
     * @param controlIdMap 复制新旧映射表
     */
    @Override
    public void copyFormSnRule(ApplicationForm source, ApplicationForm target, Map<Long, Long> controlIdMap) {
        List<ApplicationFormSnRule> sourceSnRules = this.baseMapper.findByFormId(source.getFormId());
        List<ApplicationFormSnRule> formSnRules = StreamUtil.transListT(sourceSnRules, e -> this.copyFormSnRule(e, target.getFormId(), controlIdMap));
        logService.saveAndCheck(this.baseMapper, ApplicationFormSnRule::getFormRuleId, formSnRules);
    }

    /**
     * 创建表单规则
     *
     * @param form form
     * @return ApplicationFormSnRule
     */
    @Override
    public ApplicationFormSnRule createFormSnRule(ApplicationForm form) {
        ApplicationFormSnRule applicationFormSnRule = new ApplicationFormSnRule();
        applicationFormSnRule.setEnterpriseId(form.getEnterpriseId());
        applicationFormSnRule.setFormId(form.getFormId());
        applicationFormSnRule.setIsDeleted(SwitchEnum.NO.getId());
        applicationFormSnRule.setRuleType(RuleTypeEnum.INCREMENT.getId());
        SnRuleConfigDTO snRuleConfigDTO = new SnRuleConfigDTO();
        snRuleConfigDTO.setLength(10);
        applicationFormSnRule.setSnRuleConfig(JSON.toJSONString(snRuleConfigDTO));
        applicationFormSnRule.setSort(1);

        logService.saveAndCheck(this.baseMapper, ApplicationFormSnRule::getFormRuleId, applicationFormSnRule);

        return applicationFormSnRule;
    }

    /**
     * 删除表单规则
     *
     * @param form form
     */
    @Override
    public void deleteFormSnRule(ApplicationForm form) {
        List<ApplicationFormSnRule> formSnRuleList = this.baseMapper.findByFormId(form.getFormId()).stream()
                .peek(e -> e.setIsDeleted(SwitchEnum.YES.getId()))
                .collect(Collectors.toList());

        logService.saveAndCheck(this.baseMapper, ApplicationFormSnRule::getFormRuleId, formSnRuleList);
    }

    /**
     * 生成编号
     *
     * @param applicationForm applicationForm
     * @param data            data
     * @return String
     */
    @Override
    public String generateSn(ApplicationForm applicationForm, List<ControlDataDTO> data) {
        Long enterpriseId = loginUtil.getLoginEnterpriseId();

        Map<Long, ControlDataDTO> dataMap = StreamUtil.toMapK(data, ControlDataDTO::getControlId);

        OrderSnSettingDTO snSettingBaseDTO = new OrderSnSettingDTO();
        snSettingBaseDTO.setEnterpriseId(enterpriseId);
        snSettingBaseDTO.setApplicationId(applicationForm.getApplicationId());
        snSettingBaseDTO.setType(GenerateSnEnum.APPLICATION_ORDER_SN.getType());

        if (SnRuleTypeEnum.DIY.getId() == applicationForm.getSnRuleType()) {
            // 自增值
            List<ApplicationFormSnRule> formSnRuleList = this.findByFormId(applicationForm.getFormId());

            StringJoiner formSn = new StringJoiner("");
            String increment = null;
            Date now = new Date();
            for (ApplicationFormSnRule applicationFormSnRule : formSnRuleList) {
                SnRuleRespDTO rule = new SnRuleRespDTO(applicationFormSnRule);
                SnRuleConfigDTO snRuleConfig = rule.getSnRuleConfig();
                switch (RuleTypeEnum.isExistById(rule.getRuleType())) {
                    case INCREMENT:
                        // 自增值
                        if (null == increment) {
                            snSettingBaseDTO.setResettingType(snRuleConfig.getResettingType());
                            snSettingBaseDTO.setLength(snRuleConfig.getLength());
                            increment = generateSnService.generateSn(snSettingBaseDTO);
                        }
                        formSn.add(increment);
                        break;
                    case SUBMIT_DATE:
                        // 提交日期
                        if (StringUtils.isBlank(snRuleConfig.getDateFormat())) {
                            snRuleConfig.setDateFormat(DatePattern.PURE_DATE_PATTERN);
                        }
                        try {
                            formSn.add(DateUtil.format(now, snRuleConfig.getDateFormat()));
                        } catch (Exception e) {
                            String errorMsg = String.format("【生成编号】日期格式化失败，格式为[%s]", snRuleConfig.getDateFormat());
                            log.error(errorMsg);
                            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, errorMsg);
                        }
                        break;
                    case FIXED:
                        // 固定字符
                        formSn.add(null == snRuleConfig ? "" : snRuleConfig.getFixedValue());
                        break;
                    case FORM_DATA:
                        // 表单内字段
                        if (dataMap.containsKey(snRuleConfig.getControlId())) {
                            formSn.add(dataMap.get(snRuleConfig.getControlId()).getValues().stream()
                                    .map(ControlDataValueDTO::getValue)
                                    .collect(Collectors.joining("_")));
                        }
                        break;
                    default:
                        throw RuleTypeEnum.getBusinessException();
                }
            }
            return formSn.toString();
        } else {
            // 默认
            return generateSnService.generateSn(snSettingBaseDTO);
        }
    }

    /**
     * 复制表单编号规则
     *
     * @param snRule       snRule
     * @param targetFormId targetFormId
     * @param controlIdMap controlIdMap
     * @return ApplicationFormSnRule
     */
    private ApplicationFormSnRule copyFormSnRule(ApplicationFormSnRule snRule, Long targetFormId, Map<Long, Long> controlIdMap) {
        ApplicationFormSnRule copyFormSnRule = BeanUtil.copyProperties(snRule, ApplicationFormSnRule.class);

        // 表单内字段
        if (RuleTypeEnum.FORM_DATA.getId() == copyFormSnRule.getRuleType()) {
            SnRuleConfigDTO snRuleConfigDTO = JSON.parseObject(copyFormSnRule.getSnRuleConfig(), SnRuleConfigDTO.class);
            if (null != snRuleConfigDTO.getControlId()) {
                if (!controlIdMap.containsKey(snRuleConfigDTO.getControlId())) {
                    throw new BusinessException(RCodeEnum.APPLICATION_FORM_CONTROL_NOT_EXIST);
                }
                snRuleConfigDTO.setControlId(controlIdMap.get(snRuleConfigDTO.getControlId()));
                copyFormSnRule.setSnRuleConfig(JSON.toJSONString(snRuleConfigDTO));
            }
        }
        copyFormSnRule.setFormRuleId(null);
        copyFormSnRule.setFormId(targetFormId);
        return copyFormSnRule;
    }
}
